home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
PACKET
/
PMPSRC11.ZIP
/
CONSOLE.C
< prev
next >
Wrap
Text File
|
1991-07-30
|
19KB
|
873 lines
/*
console.c -- Screen and keyboard control routines
Poor Man's Packet (PMP)
Copyright (c) 1991 by Andrew C. Payne All Rights Reserved.
Permission to use, copy, modify, and distribute this software and its
documentation without fee for NON-COMMERCIAL AMATEUR RADIO USE ONLY is hereby
granted, provided that the above copyright notice appear in all copies.
The author makes no representations about the suitability of this software
for any purpose. It is provided "as is" without express or implied warranty.
July, 1989
Andrew C. Payne
07/30/91 Temporary fix to crashing when scrollback runs out of memory
*/
/* ----- Includes ----- */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include <bios.h>
#include <alloc.h>
#include <mem.h>
#include "pmp.h"
#include "keys.h"
#define VIDEO 0x10 /* video BIOS interrupt */
/* ----- Local Stuff ----- */
static int orgcursor; /* original cursor */
static int cx,cy; /* saved cursor locations */
struct dtext { /* display text structure */
byte attr; /* display attribute */
int len; /* length of text to display */
struct dtext *next; /* next item in linked list */
byte data[1]; /* data to display */
};
static struct dtext *dtext_head; /* head of dtext queue */
static struct dtext *dtext_tail; /* tail of dtext queue */
static int dtext_mode; /* display text mode */
#define DTEXT_WRITE 1 /* write to screen */
#define DTEXT_QUEUE 2 /* queue up writes */
static byte *screen_save; /* saved screen */
static int savex, savey; /* saved cursor location */
static byte saveline[160]; /* saved message line */
struct vscrline { /* virtual screen line */
struct vscrline *next; /* next in link */
struct vscrline *prev; /* previous in link */
int len; /* length in bytes */
char data[0]; /* line data */
};
static struct vscrline *vscrfirst, *vscrlast; /* linked list */
static struct vscrline *vscrstart, *vscrend; /* start and end of current screen */
static struct vscrline *vscrorg; /* original screen */
/* ----- Low Level Screen Control ----- */
/* v_setctype(start,end)
Set the cursor size: start and end.
*/
static void cdecl v_setctype(int s,int e)
{
_AH = 1;
_CH = s;
_CL = e;
geninterrupt(VIDEO);
}
/* cursave()
Returns an integer value representing the current cursor size.
*/
int cursave(void)
{
int s,e;
_AH = 3;
_BH = 0;
geninterrupt(VIDEO);
s = _CH;
e = _CL;
return (s << 4) | e;
}
/* currest(i)
Given the number returned by 'cursave' above, restores the cursor
size.
*/
void currest(int i)
{
v_setctype(i >> 4, i & 0xF);
}
/* curoff()
Turns the cursor off.
*/
void cdecl curoff(void)
{
v_setctype(0x0f,0x0f);
}
/* curon()
Turns the cursor on.
*/
void cdecl curon(void)
{
if(monochrome)
v_setctype(12,13);
else
v_setctype(6,7);
}
/* v_scroll(n,ulrow,ulcol,lrrow,lrcol,attr)
Scrolls current display page. Blank lines are filled with attr.
Positive 'n' scrolls up, negative down.
*/
static void cdecl v_scroll(int n,int ulrow,int ulcol,int lrrow,int lrcol,int attr)
{
int mode,lines;
static int bpsave;
mode = (n > 0) ? 6 : 7; /* use vars so as not to trash regs */
lines = abs(n);
bpsave = _BP; /* old BIOS trashes BP */
_AH = mode;
_AL = lines;
_CH = ulrow;
_CL = ulcol;
_DH = lrrow;
_DL = lrcol;
_BH = attr;
geninterrupt(VIDEO);
_BP = bpsave;
}
/* putstring(x,y,len,attr,string)
Given an absolute screen position, a buffered length, and a string,
write string to screen. (FAST!!)
*/
void putstring(int x, int y, int len, byte attr, char *s)
{
byte buf[80*2]; /* buffer for string */
byte *p;
int i;
/* fill buffer with screen data */
p = buf;
for(i=0; i<len; i++) {
if(*s)
*p++ = *s++;
else
*p++ = ' '; /* buffer with spaces */
*p++ = attr; /* character attribute */
}
/* write string to screen */
puttext(x,y,x+len-1,y,buf);
}
/* clear_area(line,start,end)
Clears part of a line.
*/
void clear_area(int line, int start, int end)
{
putstring(start,line,end-start+1,0,"");
}
/* ----- CRT Entry and Exit ----- */
/* CRTInit()
Initialize the screen.
*/
void CRTInit()
{
struct text_info r;
/* Are we monochrome or CGA? */
gettextinfo(&r);
monochrome = (r.currmode == MONO);
orgcursor = cursave(); /* save original cursor */
curoff();
/* set the default screen colors */
if(monochrome) {
NormalAttr = CYAN;
MsgAttr = InvAttr = StatusAttr = 0x70;
BrightAttr = LIGHTCYAN;
} else {
NormalAttr = CYAN;
MsgAttr = InvAttr = StatusAttr = YELLOW;
BrightAttr = LIGHTCYAN;
MsgAttr = InvAttr + 0x80; /* blink */
}
cx = cy = 1;
dtext_head = dtext_tail = NULL;
dtext_mode = DTEXT_WRITE;
screen_save = NULL;
/* initialize the virtual screen stuff */
vscrfirst = vscrlast = NULL;
}
/* CRTExit()
Close up the screen.
*/
void CRTExit()
{
normal();
window(1,1,80,25);
clrscr();
currest(orgcursor); /* restore cursor */
}
/* ----- Scrollback stuff ----- */
/* TrimScreen()
Trims lines off the front of the virtual screen buffer until
coreleft() is greater than 32K.
This not the ideal way to do this. What is really needed is to have
the scrollback buffer allocated as one big chunk of memory at
startup. (acp)
*/
static void TrimScreen(void)
{
struct vscrline *p;
while(vscrfirst != NULL && coreleft() < 32000) {
p = vscrfirst->next;
free(vscrfirst);
vscrfirst = p;
}
if(vscrfirst != NULL)
vscrfirst->prev = NULL;
}
/* AddLine(n)
Adds the given line number to the scrollback buffer.
Crude version now, doesn't try to save memory by trimming lines.
*/
static void AddLine(int n)
{
struct vscrline *p;
byte temp[160]; /* temp space */
byte *q;
int len;
TrimScreen(); /* make sure we're not overflowing */
/* get line into temp space, and trim off trailing blanks */
gettext(1,n,80,n,temp);
q = temp + 158;
while(q > temp && q[0] == ' ' && q[1] == NormalAttr)
q -= 2; /* trim trailing spaces */
/* create line record */
len = q - temp + 2;
if((p = malloc(sizeof(struct vscrline) + len)) == NULL)
OutOfMemory();
memcpy(p->data,temp,p->len = len);
/* add line record to doubly-linked list */
if(vscrlast != NULL)
vscrlast->next = p;
p->prev = vscrlast;
p->next = NULL;
vscrlast = p;
if(vscrfirst == NULL)
vscrfirst = p;
}
/* PutLine(vscrline,n)
Given a pointer to a virtual line record, puts the contents of
the record on the line specified.
*/
static void PutLine(struct vscrline *p, int n)
{
puttext(1,n,p->len >> 1,n,p->data);
}
/* StartScrollback()
Starts the scrollback mode. Adds the contents of the current screen
to the scrollback buffer so it can be part of any scrolls.
Returns TRUE if in scrollback mode.
*/
int StartScrollback(void)
{
int i;
if(vscrlast == NULL)
return FALSE;
vscrorg = vscrstart = vscrlast;
for(i=1; i<24; i++) /* add rest of screen to buffer */
AddLine(i);
vscrend = vscrlast;
SaveScreen(TRUE, FALSE);
putstring(11,25,1,StatusAttr,"\031");
return TRUE;
}
/* EndSrollback()
Ends the scrollback mode. Restores current screen, and removes
current screen from buffer.
*/
void EndScrollback(void)
{
struct vscrline *p,*q;
/* remove entries representing current screen from list */
q = vscrorg->next;
while(q != NULL) {
p = q;
q = q->next;
free(p);
}
vscrlast = vscrorg; /* fix up end of list */
vscrlast->next = NULL;
RestoreScreen();
putstring(11,25,1,StatusAttr," ");
}
/* MoveScrollback(n)
Moves the scrollback the increment specified. (Negative moves
backward, positive forward).
Returns TRUE if at the end of the virtual screen.
*/
int MoveScrollback(int n)
{
if(n > 0) {
while(n-- && vscrend->next != NULL) {
v_scroll(1,0,0,22,79,NormalAttr);
PutLine(vscrend->next, 23);
if(vscrstart != NULL)
vscrstart = vscrstart->next;
else
vscrstart = vscrfirst;
vscrend = vscrend->next;
}
} else {
while(n++ && vscrstart != NULL) {
v_scroll(-1,0,0,22,79,NormalAttr);
PutLine(vscrstart, 1);
vscrstart = vscrstart->prev;
vscrend = vscrend->prev;
}
}
return (vscrend->next == NULL);
}
/* WriteScrollback(fname)
Writes the contents of the scrollback buffer to the file specified.
Error checking (on file writes) could be improved.
Also: if no info has scrolled off the top of the screen, no info
is written to the file (this needs to be fixed).
*/
void WriteScrollback(char *fname)
{
FILE *outfile;
struct vscrline *p;
char temp[85]; /* output line */
char *q,*r;
int i;
if(StartScrollback()) { /* add current screen to buffer */
outfile = fopen(fname, "w");
if(outfile == NULL) {
uprintf(InvAttr," Error: can't open '%s'\n",fname);
return;
}
p = vscrfirst; /* write buffer contents */
while(p != NULL) {
q = temp;
r = p->data;
i = p->len >> 1; /* extract data */
while(i--) {
*q++ = *r;
r += 2;
}
*q++ = '\n';
*q = '\0';
fputs(temp, outfile); /* error check this! */
p = p->next;
}
fclose(outfile);
EndScrollback(); /* restore buffer */
}
}
/* ----- Upper/Lower Screen Control ----- */
/* CheckScroll()
Check the 'cy' and scroll the screen if necessary.
*/
static void CheckScroll(void)
{
if(cy > 23) {
AddLine(1); /* save first screen line */
v_scroll(1,0,0,22,79,NormalAttr);
cy = 23;
}
}
/* GotoLeft()
If the cursor is not at the left margin, moves it there.
NOTE: Future project: this routine should queue up calls
as well in the dtext queue.
*/
void GotoLeft(void)
{
if(dtext_mode == DTEXT_WRITE) {
if(cx != 1) {
cx = 1;
cy++; /* advance row */
Capture("\n", 1); /* pass along to capture too */
}
CheckScroll();
}
}
/* GotoXY(x,y)
Moves upper screen cursor to specified location.
*/
void GotoXY(int x, int y)
{
cx = x;
cy = y;
}
/* _uputtext(attr,s,l)
Given an attribute, string and length. Does a fast screen write to
the upper screen area.
*/
static void _uputtext(byte attr, byte *s, int l)
{
byte buf[80*2]; /* buffer for string */
byte *p; /* pointer */
int x;
/* fill buffer until newline, end of string, or end of screen */
while(l) {
p = buf;
x = cx;
while(x <= 80 && l && *s != '\n') {
if(*s == '\t') { /* handle tabs */
do {
*p++ = ' ';
*p++ = attr;
x++;
} while(x <= 80 && (x % 8));
} else {
*p++ = *s;
*p++ = attr;
x++;
}
s++;
l--;
}
/* put buffer to screen */
puttext(cx,cy,x-1,cy,buf);
/* update cursor */
cx = x;
if(cx > 80 || *s == '\n') {
cx = 1;
cy++;
}
CheckScroll();
if(l && *s == '\n') {
s++;
l--;
}
}
}
/* uputtext(attr,s,l)
Given an attribute, string and length. Does a fast screen write to
the upper screen area. If the screen is not in the write mode, the
screen write is queued.
Also passes data to catpure file.
*/
void uputtext(byte attr,byte *s, int l)
{
struct dtext *q;
Capture(s, l);
if(dtext_mode == DTEXT_WRITE)
_uputtext(attr,s,l); /* write text */
else {
/* add entry to display write queue */
if((q = malloc(sizeof(struct dtext) + l)) == NULL)
OutOfMemory();
q->attr = attr;
q->len = l;
memcpy(q->data,s,l);
q->next = NULL;
if(dtext_tail != NULL)
dtext_tail->next = q;
dtext_tail = q;
if(dtext_head == NULL)
dtext_head = q;
}
}
/* uputs(attr, s)
Given a string, does a fast put to the upper screen area with the
attributes given.
*/
void uputs(byte attr, char *s)
{
int count;
int len;
char *p,*q;
/* minor hack: extract beeps */
p = q = s;
len = 0;
count = 0;
while(*q) {
if(*p != 7) {
len++;
*p++ = *q++;
} else {
count++;
q++;
}
}
if(count)
StartSound(700, 4*count); /* make some noise */
uputtext(attr, (byte *) s, len);
}
/* _uputs(attr, s)
Given a string, does a fast put to the upper screen area with the
attributes given.
NOTE: This puts routine calls the _uputtext primive, so screen
writes are never queued.
*/
void _uputs(byte attr, char *s)
{
_uputtext(attr,(byte *)s, strlen(s));
}
/* uprintf(attr, format,s)
Does a printf to the upper screen area, with given attribute.
*/
void uprintf(byte attr, char *format, ...)
{
char s[1000];
va_list argptr;
va_start(argptr, format);
vsprintf(s, format, argptr);
va_end(argptr);
uputs(attr,s);
}
/* SaveScreen(f,border)
Prepares the upper screen for an overwrite. Subsequent display writes
are queued for later processing. If 'f' is TRUE, then the contents
of the screen are preserved. If border is true, then the screen is
cleared and bordered.
*/
void SaveScreen(int f, int border)
{
int i;
byte buf[80*2];
byte *p;
dtext_mode = DTEXT_QUEUE;
if(f) {
if((screen_save = malloc(80*23*2)) == NULL)
OutOfMemory();
gettext(1,1,80,23,screen_save);
savex = cx;
savey = cy;
if(border) {
p = buf;
for(i=0; i<80; i++) {
*p++ = '─';
*p++ = NormalAttr;
}
buf[0] = '┌';
buf[79*2] = '┐';
puttext(1,1,80,1,buf);
buf[0] = '└';
buf[79*2] = '┘';
puttext(1,23,80,23,buf);
p = buf;
*p++ = '│';
p++;
for(i=0; i<78; i++) {
*p++ = ' ';
p++;
}
*p++ = '│';
for(i=2; i<23; i++)
puttext(1,i,80,i,buf);
}
}
}
/* RestoreScreen()
Restores the upper screen after an overwrite. Any queued display
writes are now processed.
*/
void RestoreScreen(void)
{
struct dtext *n;
dtext_mode = DTEXT_WRITE;
/* restore screen if previously saved */
if(screen_save != NULL) {
puttext(1,1,80,23,screen_save);
free(screen_save);
cx = savex;
cy = savey;
}
/* process all entries in the dtext queue */
while(dtext_head != NULL) {
_uputtext(dtext_head->attr,dtext_head->data,dtext_head->len);
n = dtext_head->next;
free(dtext_head);
dtext_head = n;
}
dtext_tail = NULL;
}
/* CenterTitle(line,t)
Given a title string, centers the title inversed on the line given.
*/
void CenterTitle(int line, char *t)
{
GotoXY(40 - (strlen(t) >> 1),line);
_uputs(InvAttr,t);
}
/* ----- High-Level Screen Control ----- */
/* StatusLine()
Redraws the Status line on the bottom of the screen.
*/
void StatusLine()
{
char buf[82];
char *p;
/* get user's callsign */
p = GetAX25Addr(&MyCall);
CallLength = strlen(p);
sprintf(buf," PMP %-4s│ %-9s │ ",
VERSION,p);
putstring(1,25,80,StatusAttr,buf);
}
/* ShowTXRX(tx,rx)
Shows the TX/RX state on the status line.
*/
void ShowTXRX(int tx, int rx)
{
static char *txstr[] = { " ","TX" };
static char *rxstr[] = { " ","RX" };
putstring(75,25,2,StatusAttr,txstr[tx]);
putstring(78,25,2,StatusAttr,rxstr[rx]);
}
/* ----- Link Status Change ----- */
/* NotifyStatus(old,new)
Notifies the user of the new link status.
Also updates status line to changes in link status.
*/
void NotifyStatus(int old, int new)
{
char *p,*q;
char s[70];
/* get pointer to destination path */
p = GetAX25Path(&AX25_Control.header);
/* update status line */
switch(new) {
case RECOVERY:
case CONNECTED:
sprintf(s,"»» %s",p);
putstring(13+CallLength,25,40,StatusAttr,s);
break;
case DISCONNECTED:
putstring(13+CallLength,25,40,StatusAttr,"");
break;
case SETUP:
sprintf(s,"?? %s",p);
putstring(13+CallLength,25,40,StatusAttr,s);
break;
case DISCONNECTPEND:
sprintf(s,"-- %s",p);
putstring(13+CallLength,25,40,StatusAttr,s);
break;
}
/* show change in link status */
*s = '\0';
if(new == CONNECTED && old != RECOVERY) {
sprintf(s,"***** CONNECTED to %s ",p);
StartSound(300,3);
} else if(new == DISCONNECTED) {
switch(AX25_Control.dreason) {
case DISC_LOCAL:
q = "";
break;
case DISC_REMOTE:
q = "(remote)";
break;
case DISC_TIMEOUT:
q = "(timeout)";
break;
case DISC_BUSY:
q = "(remote busy)";
break;
}
sprintf(s,"***** DISCONNECTED from %s %s ",p,q);
StartSound(200,3);
}
if(*s) /* show string */
uprintf(InvAttr,"%s\n",s);
}
/* ----- Message Line ------ */
/* Pause(t)
Waits the number of BIOS ticks specified or until a key is pressed.
Calls PeriodicHook() while waiting.
*/
void Pause(int delay)
{
long t;
t = BiosTime() + delay;
while(!keypressed() && t >= BiosTime())
PeriodicHook();
if(keypressed())
getkey();
}
/* Notify(s)
Flashes a message on the message line.
*/
void Notify(char *s)
{
int cursor;
cursor = cursave();
putstring(1,24,80,MsgAttr,s);
Pause(BIOSSEC*2); /* wait */
clear_area(24,1,80);
currest(cursor);
}
/* SaveMessage()
Saves the contents of the message line.
*/
void SaveMessage(void)
{
gettext(1,24,80,24, saveline);
}
/* RestoreMessage()
Restores the contents of the message line.
*/
void RestoreMessage(void)
{
puttext(1,24,80,24,saveline);
}
/* ----- Edit Line Input ----- */
/* GetInput(prompt,s,len)
Reads input from the editline into s, (max length 'len')
Returns TRUE if return was pressed, or FALSE if ESC abort.
*/
int GetInput(char *prompt, char *s, int len)
{
int i;
char c;
char *p;
KEY k;
int cursor;
/* show the prompt on the bottom screen line */
cursor = cursave();
curon(); /* make sure the cursor is on */
clear_area(24,1,80);
gotoxy(1,24); /* go to start */
bright();
cprintf(prompt);
p = s;
i = 0;
/* loop, handling keypresses */
while(TRUE) {
PeriodicHook(); /* handle everything else */
while(k = Nextkey()) {
if(c = asciicode(k)) {
switch(c) {
case '\r': /* CR */
case 27: /* ESC */
*p = '\0';
clear_area(24,1,80);
currest(cursor);
return c == '\r';
case '\b': /* backspace */
if(i > 0) {
i--;
p--;
cprintf("\b \b");
}
break;
default: /* store character */
if(i < len) {
*p++ = c;
i++;
bright();
putch(c);
}
break;
}
}
}
}
}